<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>flask 源码解析：请求 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="flask 源码解析：请求 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2017/01/18/flask-insight-request/">

	
	<meta property="article:published_time" content="2017-01-18T00:01:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2017/01/18/flask-insight-request/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2017-01-17T16:00:00.000Z" itemprop="datePublished">
                    2017-01-18
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">flask 源码解析：请求</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <p>这是 flask 源码解析系列文章的其中一篇，本系列所有文章列表：</p>
<ul>
<li><a href="http://cizixs.com/2017/01/10/flask-insight-introduction">flask 源码解析：简介</a></li>
<li><a href="http://cizixs.com/2017/01/11/flask-insight-start-process">flask 源码解析：应用启动流程</a></li>
<li><a href="http://cizixs.com/2017/01/12/flask-insight-routing">flask 源码解析：路由</a></li>
<li><a href="http://cizixs.com/2017/01/13/flask-insight-context">flask 源码解析：上下文</a></li>
<li><a href="http://cizixs.com/2017/01/18/flask-insight-request">flask 源码解析：请求</a></li>
<li><a href="http://cizixs.com/2017/01/22/flask-insight-response">flask 源码解析：响应</a></li>
<li><a href="http://cizixs.com/2017/03/08/flask-insight-session">flask 源码解析：session</a></li>
</ul>
<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>对于物理链路来说，请求只是不同电压信号，它根本不知道也不需要知道请求格式和内容到底是怎样的；<br>对于 TCP 层来说，请求就是传输的数据（二进制的数据流），它只要发送给对应的应用程序就行了；<br>对于 HTTP 层的服务器来说，请求必须是符合 HTTP 协议的内容；<br>对于 WSGI server 来说，请求又变成了文件流，它要读取其中的内容，把 HTTP 请求包含的各种信息保存到一个字典中，调用 WSGI app；<br>对于 flask app 来说，请求就是一个对象，当需要某些信息的时候，只需要读取该对象的属性或者方法就行了。</p>
<p>可以看到，虽然是同样的请求数据，在不同的阶段和不同组件看来，是完全不同的形式。因为每个组件都有它本身的目的和功能，这和生活中的事情一个道理：对于同样的事情，不同的人或者同一个人不同人生阶段的理解是不一样的。</p>
<p>这篇文章呢，我们只考虑最后一个内容，flask 怎么看待请求。</p>
<h2 id="请求"><a href="#请求" class="headerlink" title="请求"></a>请求</h2><p>我们知道要访问 flask 的请求对象非常简单，只需要 <code>from flask import request</code>：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">from</span> flask <span class="token keyword">import</span> request

<span class="token keyword">with</span> app<span class="token punctuation">.</span>request_context<span class="token punctuation">(</span>environ<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">assert</span> request<span class="token punctuation">.</span>method <span class="token operator">==</span> <span class="token string">'POST'</span>
</code></pre>
<p>前面<a href="http://cizixs.com/2017/01/13/flask-insight-context">一篇文章</a> 已经介绍了这个神奇的变量是怎么工作的，它最后对应了 <code>flask.wrappers:Request</code> 类的对象。<br>这个类内部的实现虽然我们还不清楚，但是我们知道它接受 WSGI server 传递过来的 <code>environ</code> 字典变量，并提供了很多常用的属性和方法可以使用，比如请求的 method、path、args 等。<br>请求还有一个不那么明显的特性——它不能被应用修改，应用只能读取请求的数据。</p>
<p>这个类的定义很简单，它继承了 <code>werkzeug.wrappers:Request</code>，然后添加了一些属性，这些属性和 flask 的逻辑有关，比如 view_args、blueprint、json 处理等。它的代码如下：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">from</span> werkzeug<span class="token punctuation">.</span>wrappers <span class="token keyword">import</span> Request <span class="token keyword">as</span> RequestBase


<span class="token keyword">class</span> <span class="token class-name">Request</span><span class="token punctuation">(</span>RequestBase<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""
    The request object is a :class:`~werkzeug.wrappers.Request` subclass and
    provides all of the attributes Werkzeug defines plus a few Flask
    specific ones.
    """</span>

    <span class="token comment" spellcheck="true">#: The internal URL rule that matched the request.  This can be</span>
    <span class="token comment" spellcheck="true">#: useful to inspect which methods are allowed for the URL from</span>
    <span class="token comment" spellcheck="true">#: a before/after handler (``request.url_rule.methods``) etc.</span>
    url_rule <span class="token operator">=</span> None

    <span class="token comment" spellcheck="true">#: A dict of view arguments that matched the request.  If an exception</span>
    <span class="token comment" spellcheck="true">#: happened when matching, this will be ``None``.</span>
    view_args <span class="token operator">=</span> None

    @property
    <span class="token keyword">def</span> <span class="token function">max_content_length</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""</span>
        ctx <span class="token operator">=</span> _request_ctx_stack<span class="token punctuation">.</span>top
        <span class="token keyword">if</span> ctx <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">return</span> ctx<span class="token punctuation">.</span>app<span class="token punctuation">.</span>config<span class="token punctuation">[</span><span class="token string">'MAX_CONTENT_LENGTH'</span><span class="token punctuation">]</span>

    @property
    <span class="token keyword">def</span> <span class="token function">endpoint</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The endpoint that matched the request.  This in combination with
        :attr:`view_args` can be used to reconstruct the same or a
        modified URL.  If an exception happened when matching, this will
        be ``None``.
        """</span>
        <span class="token keyword">if</span> self<span class="token punctuation">.</span>url_rule <span class="token keyword">is</span> <span class="token operator">not</span> None<span class="token punctuation">:</span>
            <span class="token keyword">return</span> self<span class="token punctuation">.</span>url_rule<span class="token punctuation">.</span>endpoint

    @property
    <span class="token keyword">def</span> <span class="token function">blueprint</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The name of the current blueprint"""</span>
        <span class="token keyword">if</span> self<span class="token punctuation">.</span>url_rule <span class="token operator">and</span> <span class="token string">'.'</span> <span class="token keyword">in</span> self<span class="token punctuation">.</span>url_rule<span class="token punctuation">.</span>endpoint<span class="token punctuation">:</span>
            <span class="token keyword">return</span> self<span class="token punctuation">.</span>url_rule<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span>rsplit<span class="token punctuation">(</span><span class="token string">'.'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>

    @property
    <span class="token keyword">def</span> <span class="token function">is_json</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        mt <span class="token operator">=</span> self<span class="token punctuation">.</span>mimetype
        <span class="token keyword">if</span> mt <span class="token operator">==</span> <span class="token string">'application/json'</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token boolean">True</span>
        <span class="token keyword">if</span> mt<span class="token punctuation">.</span>startswith<span class="token punctuation">(</span><span class="token string">'application/'</span><span class="token punctuation">)</span> <span class="token operator">and</span> mt<span class="token punctuation">.</span>endswith<span class="token punctuation">(</span><span class="token string">'+json'</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token boolean">True</span>
        <span class="token keyword">return</span> <span class="token boolean">False</span>
</code></pre>
<p>这段代码没有什难理解的地方，唯一需要说明的就是 <code>@property</code> 装饰符能够把类的方法变成属性，这是 python 中经常见到的用法。</p>
<p>接着我们就要看 <code>werkzeug.wrappers:Request</code>：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Request</span><span class="token punctuation">(</span>BaseRequest<span class="token punctuation">,</span> AcceptMixin<span class="token punctuation">,</span> ETagRequestMixin<span class="token punctuation">,</span>
              UserAgentMixin<span class="token punctuation">,</span> AuthorizationMixin<span class="token punctuation">,</span>
              CommonRequestDescriptorsMixin<span class="token punctuation">)</span><span class="token punctuation">:</span>

    <span class="token triple-quoted-string string">"""Full featured request object implementing the following mixins:

    - :class:`AcceptMixin` for accept header parsing
    - :class:`ETagRequestMixin` for etag and cache control handling
    - :class:`UserAgentMixin` for user agent introspection
    - :class:`AuthorizationMixin` for http auth handling
    - :class:`CommonRequestDescriptorsMixin` for common headers
    """</span>
</code></pre>
<p>这个方法有一点比较特殊，它没有任何的 body。但是有多个基类，第一个是 <code>BaseRequest</code>，其他的都是各种 <code>Mixin</code>。<br>这里要讲一下 Mixin 机制，这是 python 多继承的一种方式，如果你希望某个类可以自行组合它的特性（比如这里的情况），或者希望某个特性用在多个类中，就可以使用 Mixin。<br>如果我们只需要能处理各种 <code>Accept</code> 头部的请求，可以这样做：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Request</span><span class="token punctuation">(</span>BaseRequest<span class="token punctuation">,</span> AcceptMixin<span class="token punctuation">)</span>
    <span class="token keyword">pass</span>
</code></pre>
<p>但是不要滥用 Mixin，在大多数情况下子类继承了父类，然后实现需要的逻辑就能满足需求。</p>
<p>我们先来看看 <code>BaseRequest</code>:</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">BaseRequest</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> environ<span class="token punctuation">,</span> populate_request<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">,</span> shallow<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>environ <span class="token operator">=</span> environ
        <span class="token keyword">if</span> populate_request <span class="token operator">and</span> <span class="token operator">not</span> shallow<span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>environ<span class="token punctuation">[</span><span class="token string">'werkzeug.request'</span><span class="token punctuation">]</span> <span class="token operator">=</span> self
        self<span class="token punctuation">.</span>shallow <span class="token operator">=</span> shallow
</code></pre>
<p>能看到实例化需要的唯一变量是 <code>environ</code>，它只是简单地把变量保存下来，并没有做进一步的处理。<code>Request</code> 的内容很多，其中相当一部分是被 <code>@cached_property</code> 装饰的方法，比如下面这种：</p>
<pre class=" language-python"><code class="language-python">    @cached_property
    <span class="token keyword">def</span> <span class="token function">args</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The parsed URL parameters."""</span>
        <span class="token keyword">return</span> url_decode<span class="token punctuation">(</span>wsgi_get_bytes<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'QUERY_STRING'</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                          self<span class="token punctuation">.</span>url_charset<span class="token punctuation">,</span> errors<span class="token operator">=</span>self<span class="token punctuation">.</span>encoding_errors<span class="token punctuation">,</span>
                          cls<span class="token operator">=</span>self<span class="token punctuation">.</span>parameter_storage_class<span class="token punctuation">)</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">stream</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The stream to read incoming data from.  Unlike :attr:`input_stream`
        this stream is properly guarded that you can't accidentally read past
        the length of the input.  Werkzeug will internally always refer to
        this stream to read data which makes it possible to wrap this
        object with a stream that does filtering.
        """</span>
        _assert_not_shallow<span class="token punctuation">(</span>self<span class="token punctuation">)</span>
        <span class="token keyword">return</span> get_input_stream<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">)</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">form</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The form parameters."""</span>
        self<span class="token punctuation">.</span>_load_form_data<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>form

    @cached_property
    <span class="token keyword">def</span> <span class="token function">cookies</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""Read only access to the retrieved cookie values as dictionary."""</span>
        <span class="token keyword">return</span> parse_cookie<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">,</span> self<span class="token punctuation">.</span>charset<span class="token punctuation">,</span>
                            self<span class="token punctuation">.</span>encoding_errors<span class="token punctuation">,</span>
                            cls<span class="token operator">=</span>self<span class="token punctuation">.</span>dict_storage_class<span class="token punctuation">)</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">headers</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token triple-quoted-string string">"""The headers from the WSGI environ as immutable
        :class:`~werkzeug.datastructures.EnvironHeaders`.
        """</span>
        <span class="token keyword">return</span> EnvironHeaders<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">)</span>
</code></pre>
<p><code>@cached_property</code> 从名字就能看出来，它是  <code>@property</code> 的升级版，添加了缓存功能。我们知道<br><code>@property</code> 能把某个方法转换成属性，每次访问属性的时候，它都会执行底层的方法作为结果返回。<br><code>@cached_property</code> 也一样，区别是只有第一次访问的时候才会调用底层的方法，后续的方法会直接使用之前返回的值。<br>那么它是如何实现的呢？我们能在 <code>werkzeug.utils</code> 找到它的定义：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">cached_property</span><span class="token punctuation">(</span>property<span class="token punctuation">)</span><span class="token punctuation">:</span>

    <span class="token triple-quoted-string string">"""A decorator that converts a function into a lazy property.  The
    function wrapped is called the first time to retrieve the result
    and then that calculated result is used the next time you access
    the value.

    The class has to have a `__dict__` in order for this property to
    work.
    """</span>

    <span class="token comment" spellcheck="true"># implementation detail: A subclass of python's builtin property</span>
    <span class="token comment" spellcheck="true"># decorator, we override __get__ to check for a cached value. If one</span>
    <span class="token comment" spellcheck="true"># choses to invoke __get__ by hand the property will still work as</span>
    <span class="token comment" spellcheck="true"># expected because the lookup logic is replicated in __get__ for</span>
    <span class="token comment" spellcheck="true"># manual invocation.</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> func<span class="token punctuation">,</span> name<span class="token operator">=</span>None<span class="token punctuation">,</span> doc<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>__name__ <span class="token operator">=</span> name <span class="token operator">or</span> func<span class="token punctuation">.</span>__name__
        self<span class="token punctuation">.</span>__module__ <span class="token operator">=</span> func<span class="token punctuation">.</span>__module__
        self<span class="token punctuation">.</span>__doc__ <span class="token operator">=</span> doc <span class="token operator">or</span> func<span class="token punctuation">.</span>__doc__
        self<span class="token punctuation">.</span>func <span class="token operator">=</span> func

    <span class="token keyword">def</span> <span class="token function">__set__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> obj<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        obj<span class="token punctuation">.</span>__dict__<span class="token punctuation">[</span>self<span class="token punctuation">.</span>__name__<span class="token punctuation">]</span> <span class="token operator">=</span> value

    <span class="token keyword">def</span> <span class="token function">__get__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> obj<span class="token punctuation">,</span> type<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">if</span> obj <span class="token keyword">is</span> None<span class="token punctuation">:</span>
            <span class="token keyword">return</span> self
        value <span class="token operator">=</span> obj<span class="token punctuation">.</span>__dict__<span class="token punctuation">.</span>get<span class="token punctuation">(</span>self<span class="token punctuation">.</span>__name__<span class="token punctuation">,</span> _missing<span class="token punctuation">)</span>
        <span class="token keyword">if</span> value <span class="token keyword">is</span> _missing<span class="token punctuation">:</span>
            value <span class="token operator">=</span> self<span class="token punctuation">.</span>func<span class="token punctuation">(</span>obj<span class="token punctuation">)</span>
            obj<span class="token punctuation">.</span>__dict__<span class="token punctuation">[</span>self<span class="token punctuation">.</span>__name__<span class="token punctuation">]</span> <span class="token operator">=</span> value
        <span class="token keyword">return</span> value
</code></pre>
<p>这个装饰器同时也是实现了 <code>__set__</code> 和 <code>__get__</code> 方法的<a href="http://cizixs.com/2015/12/31/python-descriptor-introduction">描述器</a>。<br>访问它装饰的属性，就会调用 <code>__get__</code> 方法，这个方法先在 <code>obj.__dict__</code> 中寻找是否已经存在对应的值。如果存在，就直接返回；如果不存在，调用底层的函数<br><code>self.func</code>，并把得到的值保存起来，再返回。这也是它能实现缓存的原因：因为它会把函数的值作为属性保存到对象中。</p>
<p>关于 <code>Request</code> 内部各种属性的实现，就不分析了，因为它们每个具体的实现都不太一样，也不复杂，无外乎对 <code>environ</code> 字典中某些字段做一些处理和计算。<br>接下来回过头来看看 Mixin，这里只用 <code>AcceptMixin</code> 作为例子：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">AcceptMixin</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">accept_mimetypes</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> parse_accept_header<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'HTTP_ACCEPT'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> MIMEAccept<span class="token punctuation">)</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">accept_charsets</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> parse_accept_header<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'HTTP_ACCEPT_CHARSET'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                                   CharsetAccept<span class="token punctuation">)</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">accept_encodings</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> parse_accept_header<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'HTTP_ACCEPT_ENCODING'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

    @cached_property
    <span class="token keyword">def</span> <span class="token function">accept_languages</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> parse_accept_header<span class="token punctuation">(</span>self<span class="token punctuation">.</span>environ<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'HTTP_ACCEPT_LANGUAGE'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                                   LanguageAccept<span class="token punctuation">)</span>
</code></pre>
<p><code>AcceptMixin</code> 实现了请求内容协商的部分，比如请求接受的语言、编码格式、相应内容等。<br>它也是定义了很多 <code>@cached_property</code> 方法，虽然自己没有 <code>__init__</code> 方法，但是也直接使用了<br><code>self.environ</code>，因此它并不能直接使用，只能和 <code>BaseRequest</code> 一起出现。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="http://flask.pocoo.org/docs/0.12/" target="_blank" rel="noopener">Flask official docs</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="flask 源码解析：响应" href="/2017/01/22/flask-insight-response/">
        ← flask 源码解析：响应
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="flask 源码解析：上下文" href="/2017/01/13/flask-insight-context/">
        flask 源码解析：上下文 →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#简介"><span class="toc-text">简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#请求"><span class="toc-text">请求</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">flask 源码解析：请求</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2017/01/18/flask-insight-request/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2017/01/18/flask-insight-request/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
